#include "ScnsBmp.h"

#if defined(SCNS_BMP_ENABLE)&&SCNS_BMP_ENABLE==1

#include "ScnsImage.h"
#include "ScnsBuffer.h"

void scnsBmpWrite(FILE*fp,const ScnsImage*img,uint8 close)
{
    if(!img)
        return;
    SCNS_ASSERT_ON_RUN(fp);
    uint16 bitCnt=24;
    uint32 offset=54;
    if(img->format==SCNS_COLOR_FORMAT_GRAY8)
    {
        bitCnt=8;
        offset=54+1024;
    }
    else if(img->format==SCNS_COLOR_FORMAT_CLUT8)
    {
        int16 last=256;
        for(;last>0&&!img->clut[last-1];--last);
        if(last<=2)
            bitCnt=1;
        else if(last<=16)
            bitCnt=4;
        else
            bitCnt=8;
        offset=54+4*(1<<bitCnt);
    }
    const uint32 rs=((((img->width)*bitCnt)+31)>>5)<<2;
    const uint32 sizeImg=rs*img->height,fSize=offset+sizeImg;
    
    uint8*buf=malloc(fSize);
    memset(buf,0,fSize);
    scnsBufferSetUint8(buf,0,fSize,'B');
    scnsBufferSetUint8(buf,1,fSize,'M');
    scnsBufferSetUint32(buf,2,fSize,fSize);
    scnsBufferSetUint32(buf,10,fSize,offset);
    scnsBufferSetUint32(buf,14,fSize,40);            //biSize
    scnsBufferSetUint32(buf,18,fSize,img->width);
    scnsBufferSetUint32(buf,22,fSize,img->height);
    scnsBufferSetUint16(buf,26,fSize,1);             //biplanes
    scnsBufferSetUint16(buf,28,fSize,bitCnt);
    scnsBufferSetUint32(buf,34,fSize,sizeImg);
    if(img->format==SCNS_COLOR_FORMAT_GRAY8)
        for(int t=0;t<(1<<bitCnt);++t)
        {
            scnsBufferSetUint8(buf,54+t*4+0,fSize,t);
            scnsBufferSetUint8(buf,54+t*4+1,fSize,t);
            scnsBufferSetUint8(buf,54+t*4+2,fSize,t);
            scnsBufferSetUint8(buf,54+t*4+3,fSize,t);
        }
    else if(img->format==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(int t=0;t<256;++t)
        {
            scnsBufferSetUint8(buf,54+t*4+0,fSize,scnsColorArgb8888ToB(img->clut[t]));
            scnsBufferSetUint8(buf,54+t*4+1,fSize,scnsColorArgb8888ToG(img->clut[t]));
            scnsBufferSetUint8(buf,54+t*4+2,fSize,scnsColorArgb8888ToR(img->clut[t]));
            scnsBufferSetUint8(buf,54+t*4+3,fSize,0);
        }
    }
    if(bitCnt==24)
    {
        for(uint16 y=0;y<img->height;++y)
        {
            const uint32 nn=(img->height-1-y)*rs;
            for(uint16 x=0;x<img->width;++x)
            {
                const uint32 n=nn+x*3;
                uint8 r,g,b;
                img=scnsImageGetARGB(img,x,y,NULL,&r,&g,&b);
                scnsBufferSetUint8(buf,offset+n+0,fSize,b);
                scnsBufferSetUint8(buf,offset+n+1,fSize,g);
                scnsBufferSetUint8(buf,offset+n+2,fSize,r);
            }
        }
    }
    else if(bitCnt==8&&img->format==SCNS_COLOR_FORMAT_GRAY8)
    {
        for(uint32 y=0;y<img->height;++y)
        {
            const uint32 nn=(img->height-1-y)*rs;
            for(uint32 x=0;x<img->width;++x)
            {
                const uint32 n=nn+x;
                scnsBufferSetUint8(buf,offset+n,fSize,scnsImageGray8(img,x,y));
            }
        }
    }
    else if(bitCnt==8&&img->format==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(uint32 y=0;y<img->height;++y)
        {
            const uint32 nn=(img->height-1-y)*rs;
            for(uint32 x=0;x<img->width;++x)
            {
                const uint32 n=nn+x;
                scnsBufferSetUint8(buf,offset+n,fSize,scnsImageClut8(img,x,y));
            }
        }
    }
    else if(bitCnt==4&&img->format==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(uint32 y=0;y<img->height;++y)
        {
            const uint32 nn=(img->height-1-y)*rs;
            for(uint32 x=0;x<img->width;x+=2)
            {
                const uint32 n=nn+x/2;
                scnsBufferSetUint8(buf,offset+n,fSize,((scnsImageClut8(img,x,y)&0XF)<<4)|((scnsImageClut8(img,x+1,y)&0XF)));
            }
        }
    }
    else if(bitCnt==1&&img->format==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(uint32 y=0;y<img->height;++y)
        {
            const uint32 nn=(img->height-1-y)*rs;
            for(uint32 x=0;x<img->width;x+=8)
            {
                const uint32 n=nn+x/8;
                scnsBufferSetUint8(buf,offset+n,fSize,
                                   ((scnsImageClut8(img,x+0,y)&0X1)<<7)|
                                   ((scnsImageClut8(img,x+1,y)&0X1)<<6)|
                                   ((scnsImageClut8(img,x+2,y)&0X1)<<5)|
                                   ((scnsImageClut8(img,x+3,y)&0X1)<<4)|
                                   ((scnsImageClut8(img,x+4,y)&0X1)<<3)|
                                   ((scnsImageClut8(img,x+5,y)&0X1)<<2)|
                                   ((scnsImageClut8(img,x+6,y)&0X1)<<1)|
                                   ((scnsImageClut8(img,x+7,y)&0X1)<<0)
                );
            }
        }
    }
    //
    //for(uint16 y=0;y<img->height;++y)
    //    for(uint16 x=0;x<img->width;++x)
    //    {
    //        const uint32 n=(img->height-1-y)*rs+x*bitCnt/8;
    //        if(bitCnt==8)
    //        {
    //            scnsBufferSetUint8(buf,offset+n+0,fSize,scnsImageClut8(img,x,y));
    //        }
    //        else
    //        {
    //            uint8 r,g,b;
    //            img=scnsImageGetARGB(img,x,y,NULL,&r,&g,&b);
    //            scnsBufferSetUint8(buf,offset+n+0,fSize,b);
    //            scnsBufferSetUint8(buf,offset+n+1,fSize,g);
    //            scnsBufferSetUint8(buf,offset+n+2,fSize,r);
    //        }
    //    }
    fwrite(buf,fSize,1,fp);
    free(buf);
    if(close)
        fclose(fp);
}

ScnsImage*scnsBmpRead(FILE*fp,uint8 close,ScnsColorFormat type)
{
    if(!fp)
        return NULL;
    fseek(fp,0,SEEK_END);
    const uint32 fSize=ftell(fp);
    fseek(fp,0,SEEK_SET);
    
    ScnsImage*img=NULL;
    uint8*buf=malloc(fSize);
    memset(buf,0,fSize);
    if(fread(buf,1,fSize,fp)!=fSize)
        goto failed;
    if('B'!=scnsBufferGetUint8(buf,0,fSize))
        goto failed;
    if('M'!=scnsBufferGetUint8(buf,1,fSize))
        goto failed;
    if(fSize!=scnsBufferGetUint32(buf,2,fSize))
        goto failed;
    if(0!=scnsBufferGetUint32(buf,30,fSize))        //compresion
        goto failed;
    
    const uint32 offset=scnsBufferGetUint32(buf,10,fSize);
    const uint32 width=scnsBufferGetUint32(buf,18,fSize);
    const uint32 height=scnsBufferGetUint32(buf,22,fSize);
    const uint32 bitCnt=scnsBufferGetUint16(buf,28,fSize);
    const uint32 rs=((((width)*bitCnt)+31)>>5)<<2;
    if(!((offset==54&&bitCnt==24&&type!=SCNS_COLOR_FORMAT_CLUT8)||(offset==4*256+54&&bitCnt==8)||(offset==4*16+54&&bitCnt==4)||(offset==4*2+54&&bitCnt==1)))
        goto failed;
    if(!((rs*height+offset)==fSize))
        goto failed;
    img=scnsImageNew(type,width,height);
    if(bitCnt==24)
    {
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x*3;
                img=scnsImageSetARGB(img,x,y,0XFF,scnsBufferGetUint8(buf,offset+n+2,fSize),scnsBufferGetUint8(buf,offset+n+1,fSize),scnsBufferGetUint8(buf,offset+n+0,fSize));
            }
        }
    }
    else if(bitCnt==8&&type==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(uint16 t=0;t<256;++t)
            img->clut[t]=scnsColorARGBToArgb8888(0XFF,scnsBufferGetUint8(buf,54+t*4+2,fSize),scnsBufferGetUint8(buf,54+t*4+1,fSize),scnsBufferGetUint8(buf,54+t*4+0,fSize));
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x;
                const uint8 t=scnsBufferGetUint8(buf,offset+n,fSize);
                scnsImageClut8(img,x,y)=t;
            }
        }
    }
    else if(bitCnt==8)
    {
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x;
                const uint8 t=scnsBufferGetUint8(buf,offset+n,fSize);
                img=scnsImageSetARGB(img,x,y,0XFF,scnsBufferGetUint8(buf,54+t*4+2,fSize),scnsBufferGetUint8(buf,54+t*4+1,fSize),scnsBufferGetUint8(buf,54+t*4+0,fSize));
            }
        }
    }
    else if(bitCnt==4&&type==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(uint16 t=0;t<16;++t)
            img->clut[t]=scnsColorARGBToArgb8888(0XFF,scnsBufferGetUint8(buf,54+t*4+2,fSize),scnsBufferGetUint8(buf,54+t*4+1,fSize),scnsBufferGetUint8(buf,54+t*4+0,fSize));
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x/2;
                const uint8 t=(scnsBufferGetUint8(buf,offset+n,fSize)>>((1-(x&1))*4))&0XF;
                scnsImageClut8(img,x,y)=t;
            }
        }
    }
    else if(bitCnt==4)
    {
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x/2;
                const uint8 t=(scnsBufferGetUint8(buf,offset+n,fSize)>>((1-(x&1))*4))&0XF;
                img=scnsImageSetARGB(img,x,y,0XFF,scnsBufferGetUint8(buf,54+t*4+2,fSize),scnsBufferGetUint8(buf,54+t*4+1,fSize),scnsBufferGetUint8(buf,54+t*4+0,fSize));
            }
        }
    }
    else if(bitCnt==1&&type==SCNS_COLOR_FORMAT_CLUT8)
    {
        for(uint16 t=0;t<2;++t)
            img->clut[t]=scnsColorARGBToArgb8888(0XFF,scnsBufferGetUint8(buf,54+t*4+2,fSize),scnsBufferGetUint8(buf,54+t*4+1,fSize),scnsBufferGetUint8(buf,54+t*4+0,fSize));
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x/8;
                const uint8 t=(scnsBufferGetUint8(buf,offset+n,fSize)>>(7-(x&7)))&0X1;
                scnsImageClut8(img,x,y)=t;
            }
        }
    }
    else if(bitCnt==1)
    {
        for(uint32 y=0;y<height;++y)
        {
            const uint32 nn=(height-1-y)*rs;
            for(uint32 x=0;x<width;++x)
            {
                const uint32 n=nn+x/8;
                const uint8 t=(scnsBufferGetUint8(buf,offset+n,fSize)>>(7-(x&7)))&0X1;
                img=scnsImageSetARGB(img,x,y,0XFF,scnsBufferGetUint8(buf,54+t*4+2,fSize),scnsBufferGetUint8(buf,54+t*4+1,fSize),scnsBufferGetUint8(buf,54+t*4+0,fSize));
            }
        }
    }
failed:;
    free(buf);
    if(close)
        fclose(fp);
    return img;
}

#endif


